﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using SmlEngine;
using SmlEngine.Graphics;
using SmlEngine.Extensions;

namespace SmlEngine.Sprites.Base.WorldMap
{
    /// <summary>
    /// A world tile that enters a level when a WorldPlayer triggers it.
    /// </summary>
    public class LevelTile : PathTile, IWorldDrawable
    {
        #region Fields and Properties

        /// <summary>
        /// The display name of the level.
        /// </summary>
        public string LevelName { get; set; }
        /// <summary>
        /// The relative file path of the level.
        /// </summary>
        public string LevelFileName { get; private set; }
     // public object collectionState (to be implemented)
        /// <summary>
        /// A list of exits that clearing this level triggers.
        /// </summary>
        public List<Exit> Exits { get; private set; }

        private bool isAnimated { get { return worldTile.isAnimated; } }

        private bool isDataSet = false;

        /// <summary>
        /// The size of this tile.
        /// </summary>
        public override Vector2 Size
        {
            get
            {
                return worldTile.Size;
            }
        }

        /// <summary>
        /// The area of this tile.
        /// </summary>
        public new Rectangle Area
        {
            get
            {
                return new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
            }
        }
        #endregion

        #region Methods

        #region Constructors
        /// <summary>
        /// Constructs a new LevelTile instance.
        /// </summary>
        public LevelTile()
        {
            Exits = new List<Exit>();
        }

        /// <summary>
        /// Constructs a new LevelTile instance.
        /// </summary>
        /// <param name="gd">The GraphicsDevice to use (used for custom content).</param>
        /// <param name="cm">The ContentMananger to use.</param>
        /// <param name="source">The source of this tile's content.</param>
        /// <param name="cSourceRects">The positions of the tile's animation frames.</param>
        /// <param name="cFrameTime">How long, in frames, each animation frame should be displayed for.</param>
        public LevelTile(GraphicsDevice gd, ContentManager cm, string source, List<Rectangle> cSourceRects, int cFrameTime) : this()
        {
            worldTile = new WorldTile(source, cSourceRects);
            if (cSourceRects.Count > 1) IsAnimated = true; else IsAnimated = false;
            //LoadContent(gd, cm);
            Direction = PathType.FourWayIntersection;
        }

        #endregion

        #region Content Handling
        /// <summary>
        /// Loads the content for this tile.
        /// </summary>
        /// <param name="gd">The GraphicsDevice to use (used for custom content).</param>
        /// <param name="cm">The ContentMananger to use.</param>
        public new void LoadContent(GraphicsDevice gd, ContentManager cm)
        {
            worldTile.LoadContent(gd, cm, IsAnimated);
        }

        /// <summary>
        /// Unloads the content for this tile.
        /// </summary>
        public new virtual void UnloadContent() { worldTile.UnloadContent(); }
        #endregion

        #region Update and Draw
        /// <summary>
        /// Updates this tile.
        /// </summary>
        /// <param name="gameTime">Timing information for the game.</param>
        public new virtual void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        /// <summary>
        /// Draws this tile.
        /// </summary>
        /// <param name="gameTime">Timing information for the game.</param>
        /// <param name="sb">The SpriteBatch on which to draw.</param>
        public new virtual void Draw(GameTime gameTime, SpriteBatch sb)
        {
            worldTile.Draw(gameTime, sb, Position);
        }

        #endregion

        /// <summary>
        /// Adds a new exit to this level's exits.
        /// </summary>
        /// <param name="exit">The exit to add.</param>
        public void AddExit(Exit exit)
        {
            Exits.Add(exit);
        }

        /// <summary>
        /// Returns the index of an exit that matches the provided direction.
        /// </summary>
        /// <param name="d">The direction in which to search.</param>
        /// <returns>The index of the matching exit, or -2 if none are found.</returns>
        public int GetExitByDirection(Direction d)
        {
            foreach (Exit e in Exits)
            {
                if (e.ExitDirection == d) return e.Index;
            }
            return -2; // code for exit search failure
        }

        /// <summary>
        /// Returns the indexes for this tile's exits.
        /// </summary>
        /// <returns>A list of indexes.</returns>
        public List<int> GetExitIndexes()
        {
            List<int> result = new List<int>();
            foreach (Exit e in Exits)
            {
                result.Add(e.Index);
            }
            return result;
        }

        /// <summary>
        /// Sets data about this tile.
        /// </summary>
        /// <param name="position">The position of the tile on the world.</param>
        /// <param name="levelName">The display name of the level.</param>
        /// <param name="levelFilePath">The path to the level's file.</param>
        /// <param name="exits">A list of the exits for this level.</param>
        public void SetData(Vector2 position, string levelName, string levelFilePath, List<Exit> exits)
        {
            if (!isDataSet) // temporary until LevelTile uses worldTile
            {
                base.SetData(position, Int32.MinValue);
                LevelName = levelName;
                LevelFileName = levelFilePath;
                Exits = exits;
                isDataSet = true;
            }
        }
        #endregion
    }

    /// <summary>
    /// Defines information about entering a level.
    /// </summary>
    public sealed class LevelEnteredEventArgs : EventArgs
    {
        /// <summary>
        /// The relative path to the level file that this tile represents.
        /// </summary>
        public string filePath { get; private set; }
        /// <summary>
        /// The indexes of the exit that this tile triggers on clear.
        /// </summary>
        public List<int> exitNumber { get; private set; }

        /// <summary>
        /// Constructs a new LevelEnteredEventsArgs instance.
        /// </summary>
        /// <param name="c_filePath">The file path of the level to enter.</param>
        /// <param name="c_exitNumber">The indexes of the exits that can be triggered by clearing this level.</param>
        public LevelEnteredEventArgs(string c_filePath, List<int> c_exitNumber)
        {
            filePath = System.IO.Directory.GetCurrentDirectory() + c_filePath;
            exitNumber = c_exitNumber;
        }
    }

    /// <summary>
    /// Defines an indexed object that determines in which direction
    /// path tiles reveal when the corresponding level is cleared.
    /// </summary>
    public struct Exit
    {
        /// <summary>
        /// The index of this exit.
        /// </summary>
        public int Index { get; set; }
        /// <summary>
        /// The direction to reveal paths when this exit is cleared.
        /// </summary>
        public Direction ExitDirection { get; private set; }
        /// <summary>
        /// Is this exit cleared?
        /// </summary>
        public bool Cleared { get; set; }

        /// <summary>
        /// Constructs a new uncleared Exit instance.
        /// </summary>
        /// <param name="_index">The index of this exit.</param>
        /// <param name="dir">The direction to reveal paths when this exit is cleared.</param>
        public Exit(int _index, Direction dir) : this()
        {
            Index = _index;
            ExitDirection = dir;
            Cleared = false;
        }

        /// <summary>
        /// Constructs a new Exit instance.
        /// </summary>
        /// <param name="_index">The index of this exit.</param>
        /// <param name="dir">The direction to reveal paths when this exit is cleared.</param>
        /// <param name="_cleared">Sets the cleared state of this exit.</param>
        public Exit(int _index, Direction dir, bool _cleared)
            : this(_index, dir)
        {
            Cleared = _cleared;
        }

        /// <summary>
        /// Constructs a new uncleared Exit instance from an INI-style file entry.
        /// </summary>
        /// <param name="fileEntry">The file entry to use.</param>
        public Exit(string fileEntry) : this()
        {
            Index = int.Parse(fileEntry.Substring(0, fileEntry.Length - 1));
            string dir = fileEntry[fileEntry.Length - 1].ToString();
            if (dir == "u") ExitDirection = Direction.Top;
            else if (dir == "d") ExitDirection = Direction.Bottom;
            else if (dir == "l") ExitDirection = Direction.Left;
            else if (dir == "r") ExitDirection = Direction.Right;
            Cleared = false;
        }

        /// <summary>
        /// Constructs a new Exit instance from an INI-style file entry.
        /// </summary>
        /// <param name="fileEntry">The file entry to use.</param>
        /// <param name="_cleared">Sets the cleared state of this exit.</param>
        public Exit(string fileEntry, bool _cleared)
            : this(fileEntry)
        {
            Cleared = _cleared;
        }
    }
}
